#!/usr/bin/env python

from os3.types import money
import sys, re

class LiWEUtils ( object ):
	SAFE_UNSET = 0 
	SAFE_NUMBER = 1
	SAFE_STRING = 2
	SAFE_DB_STRING = 3
	SAFE_MONEY = 4
	SAFE_BOOL = 5
	SAFE_DATE = 6
	SAFE_DB_BOOL = 7
	SAFE_HTML = 8
	SAFE_DB_HTML = 9
	SAFE_EMAIL = 10

	def __init__ ( self, liwe ):
		super ( LiWEUtils, self ).__init__ ()
		self.liwe = liwe

	def validate ( self, mode, value ):
		"""
		``mode``:
			- ``email``
		"""
		value = str ( value ).strip ()
		if mode == 'email':
			if re.match ( r"^[-A-Za-z0-9_\.]+@[-a-zA-Z0-9_\.]+\.[a-zA-Z]{2,4}$", value ): return True

		return False

	def safe_get_mode ( self, name ):
		return {
			"NUMBER" : self.SAFE_NUMBER,
			"STRING" : self.SAFE_STRING,
			"DB_STRING" : self.SAFE_DB_STRING,
			"MONEY"	    : self.SAFE_MONEY,
			"BOOL"	    : self.SAFE_BOOL,
			"DATE"	    : self.SAFE_DATE,
			"DB_BOOL"   : self.SAFE_DB_BOOL,
			"HTML"	    : self.SAFE_HTML,
			"DB_HTML"   : self.SAFE_DB_HTML,
			"EMAIL"	    : self.SAFE_EMAIL,
		}.get ( name.upper (), 0 )

	def safe_get ( self, name, mode = "PGS", safe = 0, def_val = '', def_user_val = 0xDEADBEEF ):
		def _map_entities ( txt ):
			"""
			for mod in [
					[ "&#8217;", "'" ],
					[ "&#224;", "&agrave;" ],
					[ "&#232;", "&egrave;" ],
					[ "&#233;", "&eacute;" ],
					[ "&#242;", "&ograve;" ],
					[ "&#249;", "&ugrave;" ],
					[ "&#8220;", '"' ],
					[ "&#8221;", '"' ],
					[ "&#8211;", "-" ],
					[ "%u2013", "-" ],
					[ "%u2019", "'" ],
					[ "%u201C", '"' ],
					[ "%u201D", '"' ]
				]:
				txt = txt.replace ( mod [ 0 ], mod [ 1 ] )
			"""

			return txt

		for m in mode:
			if m == 'U':
				val = def_user_val
			elif m == 'P':
				val = self.liwe.pyhp.post.get ( name, 0xDEADBEEF )
			elif m == 'G':
				val = self.liwe.pyhp.get.get ( name, 0xDEADBEEF )
			elif m == 'S':
				val = self.liwe.pyhp.session.data.get ( name, 0xDEADBEEF )

			if val != 0xDEADBEEF:
				break

		if val == 0xDEADBEEF:
			return def_val

		if not safe:
			return val

		if hasattr ( val, 'strip' ):
			val = val.strip ()
		res = _map_entities ( val )

		if safe == self.SAFE_NUMBER:
			try:
				res = int ( val )
			except:
				res = 0

		elif safe == self.SAFE_DB_STRING:
			if isinstance ( res, basestring ):
				res = res.replace ( "'", "''" ).replace ( "\\", "\\\\" )

		elif safe == self.SAFE_MONEY:
			m = money.Money ( val, tsep = ".", dsep = "," )
			res = m.to_long_int ()

		elif safe == self.SAFE_BOOL:
			if val in ( 'false', '0', 0 ): 
				val = 0

			if val: res = True
			else: res = False

		elif safe == self.SAFE_DB_BOOL:
			if val in ( 'false', '0', 0 ): 
				val = 0

			if val: res = 1
			else: res = 0

		elif safe == self.SAFE_DATE:
			# FIXME: da fare
			res = val.replace ( "'", "''" ).replace ( "\\", "\\\\" )

		elif safe == self.SAFE_HTML:
			# FIXME: da fare
			res = _map_entities ( val )

		elif safe == self.SAFE_DB_HTML:
			# FIXME: da fare
			res = _map_entities ( val )
			if isinstance ( res, basestring ):
				res = res.replace ( "'", "''" ).replace ( "\\", "\\\\" )

		elif safe == self.SAFE_EMAIL:
			# FIXME: da finire
			if isinstance ( res, basestring ):
				res = val.replace ( "'", "" )
			
		return res

	def safe_multi ( self, fields, mode = "PGS" ):
		res = {}

		for f in fields:
			name = f [ 0 ]
			safe = 0
			def_val = ''

			if len ( f ) > 1: safe = f [ 1 ]
			if len ( f ) > 2: def_val = f [ 2 ]

			res [ name ] = self.safe_get ( name, mode, safe, def_val )

		return res


	def string_resize ( self, s, max_len, do_entities = True ):

		def _calc_ellipsis ( s, max_len ):
			l = len ( s )
			if l <= max_len: return s

			pos = s [ : max_len ].rfind ( " " )

			if pos < 0:
				pos = max_len
			else:
				pos += 1

			return s [ : pos ] + "..."

		entities = {
			"&nbsp;":   "\xC0",

			"&agrave;": "\xC1",
			"&egrave;": "\xC2",
			"&igrave;": "\xC3",
			"&ograve;": "\xC4",
			"&ugrave;": "\xC5",

			"&Agrave;": "\xC6",
			"&Egrave;": "\xC7",
			"&Igrave;": "\xC8",
			"&Ograve;": "\xC9",
			"&Ugrave;": "\xCA",

			"&aacute;": "\xCB",
			"&eacute;": "\xCC",
			"&iacute;": "\xCD",
			"&oacute;": "\xCE",
			"&uacute;": "\xCF",

			"&Aacute;": "\xD0",
			"&Eacute;": "\xD1",
			"&Iacute;": "\xD2",
			"&Oacute;": "\xD3",
			"&Uacute;": "\xD4"
		}

		if max_len < 4: return s

		max_len -= 3

		s = re.sub ( "<[^>]+>", "", s )
		s = re.sub ( "[ \t\r\n]+", " ", s )

		if do_entities:
			for entity, trans_char in entities.iteritems():
				s = s.replace ( entity, trans_char )

		s = _calc_ellipsis ( s, max_len )

		if do_entities:
			for entity, trans_char in entities.iteritems ():
				if entity == "&nbsp;": continue
				s = s.replace ( trans_char, entity )

			s = s.replace ( "\xC0", " " )

		return s

class UpdatePos:
	def __init__ ( self, dbm, tbl, cfg_fields, module_name = '' ):
		self.dbm = dbm
		self.cfg = cfg_fields # { id: table id field, cat: table cat field, pos: table position field }
		self.cfg [ 'fields' ] = [ v for k, v in cfg_fields.iteritems () ] 
		self.cfg [ 'tbl' ] = tbl
		self.module_name = module_name

	def _find_max_pos ( self, id_categ ):
		cfg = self.cfg 
		conds = []
		conds.append ( "%s = '%s'" % ( cfg [ 'cat' ], id_categ ) )
		if cfg.get ( 'module' ):
			conds.append ( "%s = '%s'" % ( cfg [ 'module' ], self.module_name ) )
		#res = self.dbm.single_row ( cfg [ 'tbl' ], "MAX( %s ) as max_pos" % cfg [ 'pos' ], "%s = '%s'" % ( cfg [ 'cat' ], id_categ ) )
		res = self.dbm.single_row ( cfg [ 'tbl' ], "MAX( %s ) as max_pos" % cfg [ 'pos' ], ' AND '.join ( conds ) ) 
		if not res:
			return 0
		return res [ 'max_pos' ]

	def _update_pos ( self, id_obj, id_categ = 0, pos = 0, max_pos = 0 ):
	
		if not id_categ and pos != -1: return
	
		cfg = self.cfg

		conds = []

		if cfg.get ( 'module' ):
			conds.append ( "%s = '%s'" % ( cfg [ 'module' ], self.module_name ) )

		conds.append ( "%s = '%s'" % ( cfg [ 'id' ], id_obj ) )

		cat_max = self._find_max_pos ( id_categ )
		orig = self.dbm.single_row ( cfg [ 'tbl' ], cfg [ 'fields' ], ' AND '.join ( conds ) )

		if not orig:
			conds.pop ()
			conds.append ( "%s = '%s'" % ( cfg [ 'id' ], id_obj ) )
			# new insert
			self.dbm.update ( cfg [ 'tbl' ], { cfg [ 'pos' ] : cat_max + 1 }, ' AND '.join ( conds ) )
			return True
		if pos == -1:

			conds.pop ()
			conds.append ( "%s > %s AND %s = '%s'" %  ( cfg [ 'pos' ], orig [ cfg [ 'pos' ] ], cfg [ 'cat' ], orig [ cfg [ 'cat' ] ] ) ) 
			# delete case	
			self.dbm.update ( cfg [ 'tbl' ], { cfg [ 'pos' ] : '*pos-1' }, ' AND '.join ( conds ) ) 
			return True		

		if orig [ cfg [ 'pos' ] ] != pos and str ( orig [ cfg [ 'cat' ] ] ) == str ( id_categ ):

			conds.pop ()
			chk = 0
			# update cases
			if orig [ cfg [ 'pos' ] ] < pos and pos <= cat_max:

				conds.append ( "( %s BETWEEN %s AND %s ) AND %s = '%s'" % ( cfg [ 'pos' ], orig [ cfg [ 'pos' ] ] , pos, cfg [ 'cat' ], id_categ ) )
				self.dbm.update ( cfg [ 'tbl' ], { cfg [ 'pos' ] : '*pos-1' }, ' AND '.join ( conds ) )
				chk = 1
			#FIXME: pos >= 0 to be compatible with liwe tree it should be pos > 0
			if orig [ cfg [ 'pos' ] ] > pos and pos >= 0:

				conds.append ( "( %s BETWEEN %s AND %s ) AND %s = '%s'" % ( cfg [ 'pos' ], pos, orig [ cfg [ 'pos' ] ], cfg [ 'cat' ], id_categ ) ) 
				self.dbm.update ( cfg [ 'tbl' ], { cfg [ 'pos' ] : '*pos+1' }, ' AND '.join ( conds ) ) 
				chk = 1

			if chk: 
				conds.pop ()
				conds.append ( "%s = '%s'" % ( cfg [ 'id' ], id_obj ) )
				self.dbm.update ( cfg [ 'tbl' ], { cfg [ 'pos' ] : pos }, ' AND '.join ( conds ) )

		if str ( orig [ cfg [ 'cat' ] ] ) != str ( id_categ ):
			# change of category
			conds.pop ()
			conds.append ( "%s > %s AND %s = '%s'" % ( cfg [ 'pos' ], orig [ cfg [ 'pos' ] ], cfg [ 'cat' ], orig [ cfg [ 'cat' ] ] ) )
			self.dbm.update ( cfg [ 'tbl' ], { cfg [ 'pos' ] : '*pos-1' }, ' AND '.join ( conds ) )
			cat_max = cat_max + 1
			#FIXME: check on edit function
			# return cat_max to calling function
			return cat_max
		#self.dbm.debug = None
		return False	
